 /*
    *  Copyright 2004 Thevert Liu  <thevert.liu@gmail.com>
    *
    *  Licensed under the Apache License, Version 2.0 (the "License");
    *  you may not use this file except in compliance with the License.
    *  You may obtain a copy of the License at
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
   *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */


#include <LibXML2Reader.h>
#include <x2log.h>

LibXML2Reader::LibXML2Reader(XMLContext * ctx)
  : IXMLReader(ctx), mReader(NULL), mNeedRead(true)
{
}

LibXML2Reader::~LibXML2Reader() 
{
    xmlFreeTextReader (mReader) ;
    xmlCleanupParser () ;
}

bool LibXML2Reader::create(const std::string& xmlbody, const std::string& roottag)
{
    if (xmlbody.empty() || roottag.empty()) {
        s3crit << "xmlbody or root tag is empty" << ENDL ;
        return false ;
    }
    
    xmlTextReaderPtr reader;

    const char* UTF8_CANONICALNAME = xmlGetCharEncodingName(XML_CHAR_ENCODING_UTF8);
    if (UTF8_CANONICALNAME == NULL) {
        //s3Crit("Unable to obtain the canonical name for the UTF-8 encoding scheme. Reverting to default, UTF-8\n");
        UTF8_CANONICALNAME = "UTF-8";
    }
    
    size_t bodysize = xmlbody.size() ;
    reader = xmlReaderForMemory(xmlbody.c_str(),
            bodysize,
            NULL,
            UTF8_CANONICALNAME,
            0);
    
    if (reader == NULL) {
        s3crit << "xmlreader create failed!" << ENDL ;
        return false ; // Create xml reader error!
    }

    mReader = reader ;
    
    std::string tag ;

    while (nextElement(tag)) {
        s3debug << "Find element:" << tag << ENDL ;
        if (!isElement()) {
            s3debug << "Is not a element." << ENDL ;
            continue ;
        }
        if (roottag == tag) {
            return  true;
        }
    }
    
    
    s3crit << "Not find start tag:" << roottag << ENDL ;
    
    return false ;    

}

bool LibXML2Reader::getAttribute(const std::string& name, std::string& val) const
{
    xmlChar* attrVal = xmlTextReaderGetAttribute(mReader,
                                           BAD_CAST name.c_str());
    if (attrVal == NULL) {
        return false;
    }
    val = (char*) attrVal ;
    // free xmlChar* as instructed in comments for xmlTextReaderGetAttribute
    xmlFree(attrVal);
    return true;
}

bool LibXML2Reader::getExpandData(std::string& val) const
{
    xmlChar* buffer = xmlTextReaderReadInnerXml(mReader) ;
    if (buffer==NULL) {
        return false ;
    }
    
    val = (char*) buffer ;
    xmlFree(buffer) ;
    
    xmlTextReaderNext(mReader) ;
    
    ((LibXML2Reader*)this)->mNeedRead = false ;
    
    return true;
}


bool LibXML2Reader::getValue(std::string& val) const
{
    if (!xmlTextReaderHasValue(mReader)) {
        s3crit << "Current node in reader isn't a value!" << ENDL ;
        return false;
    }

    val = (char*)(xmlTextReaderConstValue(mReader)) ;

    if (val.empty()) {
        s3crit << "Value is empty!" << ENDL ;
        return false;
    }
    
    return true ;
}

bool LibXML2Reader::nextElement(std::string& name) const
{
    if (mNeedRead) { // for extrablock has go to the next node.
        s3debug << "Reade next" << ENDL ;
        int ret = xmlTextReaderRead(mReader) ;
        
        if (ret!=1) {
           s3crit << "xmlTextReaderRead failed!" << ENDL ;
           return false;
        }
    } else {
        s3debug << "Ignal a read" << ENDL ;
        ((LibXML2Reader*)this)->mNeedRead = true ;
    }
    
    const char* t = (char*)xmlTextReaderConstName(mReader);

    if (t==NULL) {
        return false ;
    }
    
    name = t ;
    /// name maybe empty (as value type).
    
    return true ;
}

int  LibXML2Reader::getTreeStartDepth() const
{
    return xmlTextReaderDepth(mReader);
}



bool LibXML2Reader::getParentName(std::string& name) const
{
    name = (const char*) xmlTextReaderConstName(mReader);
    return true;
}

bool LibXML2Reader::getNSName(std::string& ns, std::string& name) 
{
    name = (const char*) xmlTextReaderConstLocalName(mReader) ;
    ns = (const char*) xmlTextReaderConstNamespaceUri(mReader) ;
    return true ;
}

bool LibXML2Reader::isValue() const
{
    int type = xmlTextReaderNodeType(mReader);
    return type==XML_READER_TYPE_TEXT;
}

bool LibXML2Reader::isElement() const
{
    int type = xmlTextReaderNodeType(mReader);
    return type==XML_READER_TYPE_ELEMENT;
}

